/**
 *******************************************************************************
 * @file system_init.c
 * @author .ISS_AUTO (hudandan@issauto.com)
 * @date 2024-05-15  1. Base Version.
 *       2024-09-04  1. Version: 0.1.0.
 *
 * @brief  This file for the System layer initialization specification.
 *
 * @copyright Copyright (c) 2024 ISSAUTO TECH Co., Ltd. All rights reserved.
 *
 *******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "system_init.h"

/* Exported Function ---------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
RCC_ClocksType sRCC_ClockFreq;

#if (IWDG_ENABLE)
    __IO uint32_t SYSCLK_LSI_FREQ = 40000; //!< 40KHz 的低速内部时钟（LSI 时钟）
#endif

#if (LSI_TIM_MEASURE)
    __IO uint16_t IC1ReadValue1, IC1ReadValue2;
    __IO uint16_t LSI_CaptureNumber;
    __IO uint32_t Capture;
    __IO uint32_t C_LsiFreq = 0;
#endif

/* Private variables ---------------------------------------------------------*/
static ErrorStatus HSEStartUpStatus;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/* PRQA S 2740, 2870, 2885, 3383 ++ */

/*******************************************************************************
 *                            General Functions
********************************************************************************/
/**
 * @brief Display the hexadecimal values of a register
 *
 * @param data Pointer to the data buffer
 * @param len Length of the data
 */
void i_showUtf8Hex(uint8_t *data, uint32_t len)
{
    if (len == 0) return;

    for (uint32_t i = 0; i < len; i++) {
        printf("%02X ", data[i]);
    }

    // printf("\r\n");
}

/*******************************************************************************
 *                             Error Handler
********************************************************************************/

/**
 * @brief System reset
 *
 * @param errorCode
 */
void User_SystemReset(void)
{
    __set_FAULTMASK(1); /* 关闭所有中端 */
    NVIC_SystemReset(); /* 复位 */

    #if (0)
    /* 采用看门狗实现软件复位 */
    IWDG_CntReload(SYSCLK_LSI_FREQ / 3200);

    while (1);

    #endif
}

/**
 * @brief  This function is executed in case of DRV error occurrence.
 * @retval None
 */
#if !defined (USE_TESSY_TEST)
void DRV_Error_Handler(uint32_t errorCode)
{
    #if (DEBUG_FLASH_FAULT_CODE)
    (void)errorCode;
    #endif

    User_SystemReset();
}

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
    DRV_Error_Handler(APP_ERR_CODE_RST_DEV);

    /* User can add his own implementation to report the HAL error return state */
    while (1) {
        ;
    }
}
#endif

#ifdef USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param file pointer to the source file name
 * @param line assert_param error line source number
 */
void assert_failed(const uint8_t *expr, const uint8_t *file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line
       number, ex: printf("Wrong parameters Value: file %s on line %d\r\n",
       file, line) */

    /* Infinite loop */
    while (1) {
    }
}
#endif /* USE_FULL_ASSERT */

/*******************************************************************************
 *                             PVD Configuration
********************************************************************************/

#if (PVD_ENABLE)
/**
  * @brief PVD Initialization Function
  * @param None
  * @retval None
  */
void PVD_Configuration(void)
{
    /* Enable PWR and BKP clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);

    /* Configure EXTI Line to generate an interrupt on falling edge */
    {
        EXTI_InitType EXTI_InitStructure;

        /* Configure EXTI Line16(PVD Output) to generate an interrupt on rising and
           falling edges */
        EXTI_ClrITPendBit(EXTI_LINE16);
        EXTI_InitStructure.EXTI_Line    = EXTI_LINE16;
        EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
        EXTI_InitPeripheral(&EXTI_InitStructure);
    }

    /* NVIC configuration */
    {
        NVIC_InitType NVIC_InitStructure;

        /* Configure one bit for preemption priority */
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

        /* Enable the PVD Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel                   = PVD_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0x00;
        NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    }

    /* Configure the PVD Level to 2.9V */
    PWR_PvdRangeConfig(PWR_PVDRANGE_3V06);

    /* Enable the PVD Output */
    PWR_PvdEnable(ENABLE);
}
#endif

/*******************************************************************************
 *                             Backup Configuration
********************************************************************************/

/**
 * @brief Backup Configuration
 * @param None
 * @retval None
 */
void Backup_Configuration(void)
{
    /* Enable PWR and BKP clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);

    /* Enable write access to Backup domain */
    PWR_BackupAccessEnable(ENABLE);
}

/*******************************************************************************
 *                             LowPower Configuration
********************************************************************************/
#if (LPM_ENABLE)

/**
 * @brief LPM interrupt callback function
 *
 * @param flag
 */
void LPM_FlagClear(void)
{
    /* Clear WakeUp Flag from BKP_REG_ID_WKUP_FLAG */
    BKP_WriteBkpData(BKP_REG_ID_WKUP_FLAG, 0x0000);
}

/**
 * @brief LPM interrupt callback function
 *
 * @param flag
 */
void LPM_IntCallback(uint16_t flag)
{
    uint16_t tmpData = 0;

    /* Enable PWR and BKP clock */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_PWR | RCC_APB1_PERIPH_BKP, ENABLE);

    /* Enable write access to Backup domain */
    PWR_BackupAccessEnable(ENABLE);

    tmpData = BKP_ReadBkpData(BKP_REG_ID_WKUP_FLAG);

    BKP_WriteBkpData(BKP_REG_ID_WKUP_FLAG, tmpData | flag);
}

__IO uint16_t WKUP_TRIGGER_DAT; /*!< 唤醒标志 */

void WKUP_FLAG_PRINTF(uint16_t flag)
{
    USER_PRINTF("\r\n#[>3<] [WKUP_FLAG] < %04X >", WKUP_TRIGGER_DAT);

    if (flag == BKP_DATA_WKUP_FLAG_DEFAULT) {
        USER_PRINTF("# DEFAULT #");
    } else if (flag == BKP_DATA_WKUP_FLAG_EXIT_PA0) {
        USER_PRINTF("# PA0 #");
    }

    #if defined(BKP_DATA_WKUP_FLAG_EXIT_RX)
    else if (flag == BKP_DATA_WKUP_FLAG_EXIT_RX) {
        USER_PRINTF("# RX #");
    }

    #endif
    else if (flag == BKP_DATA_WKUP_FLAG_WDG) {
        USER_PRINTF("# WDG #");
    } else if (flag == 0xFFFF) {
        USER_PRINTF("# ERROR #");
        flag = BKP_DATA_WKUP_FLAG_DEFAULT;
    }

    USER_PRINTF("\r\n");
}

/**
 * @brief LPM interrupt callback function
 *
 * @param flag
 */
uint16_t LPM_WkupFlagRead(void)
{
    uint16_t Sys_Wake_Flag;

    WKUP_TRIGGER_DAT = BKP_ReadBkpData(BKP_REG_ID_WKUP_FLAG);
    Sys_Wake_Flag = 0x0000;

    if (WKUP_TRIGGER_DAT == 0x0000) {
        /* First PowerOn */
        Sys_Wake_Flag = BKP_DATA_WKUP_FLAG_DEFAULT;
    } else if (!(WKUP_TRIGGER_DAT & BKP_DATA_WKUP_FLAG_MASK)) {
        uint16_t Wkup_Data_Update = 0x0000;

        if (WKUP_TRIGGER_DAT & BKP_DATA_WKUP_FLAG_EXIT_PA0) {
            Sys_Wake_Flag = BKP_DATA_WKUP_FLAG_EXIT_PA0;
        }

        #if defined(BKP_DATA_WKUP_FLAG_EXIT_RX)
        else if (WKUP_TRIGGER_DAT & BKP_DATA_WKUP_FLAG_EXIT_RX) {
            Sys_Wake_Flag = BKP_DATA_WKUP_FLAG_EXIT_RX;
        }

        #endif
        else if (WKUP_TRIGGER_DAT & BKP_DATA_WKUP_FLAG_WDG) {
            Sys_Wake_Flag = BKP_DATA_WKUP_FLAG_WDG;
        } else {
            /* Error! Clear WKUP_FLAG. */
            Sys_Wake_Flag = 0xFFFF;
        }

        Wkup_Data_Update = WKUP_TRIGGER_DAT & ~Sys_Wake_Flag;
        BKP_WriteBkpData(BKP_REG_ID_WKUP_FLAG, Wkup_Data_Update); //!< Update the WKUP_FLAG.
    } else {
        /* Error! Clear WKUP_FLAG. */
        Sys_Wake_Flag = 0xFFFF;
        BKP_WriteBkpData(BKP_REG_ID_WKUP_FLAG, 0x0000); //!< Update the WKUP_FLAG.
    }

    WKUP_FLAG_PRINTF(Sys_Wake_Flag);

    return Sys_Wake_Flag;
}

#endif

/*******************************************************************************
 *                             LSI TIM Measure
********************************************************************************/
#if (LSI_TIM_MEASURE)

/**
 * @brief  Configures TIM5 to measure the LSI oscillator frequency.
 * @param None
 * @retval None
 */
static void TIM5_ConfigForLSI(void)
{
    NVIC_InitType NVIC_InitStructure;
    TIM_ICInitType TIM_ICInitStructure;

    /* Enable TIM5 clocks */
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM5, ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Enable the TIM5 Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel                   = TIM5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* Configure TIM5 prescaler */
    TIM_ConfigPrescaler(TIM5, 0, TIM_PSC_RELOAD_MODE_IMMEDIATE);

    /* Connect internally the TM5_CH4 Input Capture to the LSI clock output */
    GPIO_ConfigPinRemap(GPIO_RMP_TIM5CH4, ENABLE);

    /* TIM5 configuration: Input Capture mode
       The LSI oscillator is connected to TIM5 CH4
       The Rising edge is used as active edge,
       The TIM5 CCDAT4 is used to compute the frequency value */
    TIM_ICInitStructure.Channel     = TIM_CH_4;
    TIM_ICInitStructure.IcPolarity  = TIM_IC_POLARITY_RISING;
    TIM_ICInitStructure.IcSelection = TIM_IC_SELECTION_DIRECTTI;
    TIM_ICInitStructure.IcPrescaler = TIM_IC_PSC_DIV8;
    TIM_ICInitStructure.IcFilter    = 0;
    TIM_ICInit(TIM5, &TIM_ICInitStructure);

    /* TIM5 Counter Enable */
    TIM_Enable(TIM5, ENABLE);

    /* Reset the flags */
    TIM5->STS = 0;

    /* Enable the CC4 Interrupt Request */
    TIM_ConfigInt(TIM5, TIM_INT_CC4, ENABLE);
}

/**
  * @brief LSI TIM Measure Configuration
  * @param None
  * @retval None
  */
void LSI_TIM_Measure(void)
{
    /** @note LSI 校准步骤如下：
     * 1. 打开 TIM5，设置通道 4 为输入捕获模式；
     * 2. 设置 AFIO_RMP_CFG.TIM5CH4_RMP 位为 1，将 LSI 内部连接到 TIM5 的通道 4；
     * 3. 通过 TIM5 捕获/比较 4 事件或中断测量 LSI 时钟频率
     * 4. 根据测量结果和所需的 RTC 时基和独立的看门狗超时，设置 20 位预分频器。
     */
    IC1ReadValue1 = 0;
    IC1ReadValue2 = 0;
    LSI_CaptureNumber = 0;
    Capture = 0;

    /* TIM Configuration */
    TIM5_ConfigForLSI();

    /* Wait until the TIM5 get 3 LSI edges */
    while (LSI_CaptureNumber < 3) {
        ;
    }

    /* Disable TIM5 CC4 Interrupt Request */
    TIM_ConfigInt(TIM5, TIM_INT_CC4, DISABLE);

    /* TIM5 Counter Enable */
    TIM_Enable(TIM5, DISABLE);
}
#endif


/*******************************************************************************
 *                             IWDG Configuration
********************************************************************************/
#if (IWDG_ENABLE)

/**
 * @brief IWDG Configuration
 * @param None
 * @retval None
 */
void IWDG_Configuration(void)
{
    uint16_t IWDG_Reload;

    #if (LPM_ENABLE)

    /* Check if the system has resumed from IWDG reset */
    if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET) {
        /* IWDGRST flag set -> Clear reset flags */
        RCC_ClrFlag();

        LPM_IntCallback(BKP_DATA_WKUP_FLAG_WDG);
    } else {
        ; /* IWDGRST flag is not set */
    }

    #endif

    /* Enable the LSI OSC */
    RCC_EnableLsi(ENABLE);

    /* Wait till LSI is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_LSIRD) == RESET) {
        ;
    }

    // #if (LSI_TIM_MEASURE)
    // /*!< LSI时钟校准 */
    // LSI_TIM_Measure();
    // SYSCLK_LSI_FREQ = C_LsiFreq;
    // #endif

    /* IWDG timeout equal to 250 ms (the timeout may varies due to LSI frequency
       dispersion) */
    /* Enable write access to IWDG_PR and IWDG_RLR registers */
    IWDG_WriteConfig(IWDG_WRITE_ENABLE);

    #if (0) /* 0.2ms ~ 819.2ms */
    /* IWDG counter clock: LSI/8 */
    IWDG_SetPrescalerDiv(IWDG_PRESCALER_DIV8);

    /** Set counter reload Value to obtain [25ms] IWDG TimeOut.
     * Counter Reload Value = 25ms / IWDG counter clock period
     *                      = 0.025s / (1/(SYSCLK_LSI_FREQ/8))
     *                      = 0.025s * (SYSCLK_LSI_FREQ/8)
     *                      = (1/40) * (SYSCLK_LSI_FREQ/8)
     *                      = SYSCLK_LSI_FREQ/320
     */
    IWDG_Reload = SYSCLK_LSI_FREQ / 320;

    #elif (0) /* 0.8ms ~ 3276.8ms */
    /* IWDG counter clock: LSI/32 */
    IWDG_SetPrescalerDiv(IWDG_PRESCALER_DIV32);

    /** Set counter reload Value to obtain [100ms] IWDG TimeOut.
     * Counter Reload Value = 100ms / IWDG counter clock period
     *                      = 0.1s / (1/(SYSCLK_LSI_FREQ/32))
     *                      = (1/10) * (SYSCLK_LSI_FREQ/32)
     *                      = SYSCLK_LSI_FREQ/320
     */
    IWDG_Reload = SYSCLK_LSI_FREQ / 320;

    #elif (1) /* 3.2ms ~ 13107.2ms */
    /* IWDG counter clock: LSI/128 */
    IWDG_SetPrescalerDiv(IWDG_PRESCALER_DIV128);

    /** Set counter reload Value to obtain [10s] IWDG TimeOut.
     * Counter Reload Value = 10s / IWDG counter clock period
     *                      = 10s / (1/(SYSCLK_LSI_FREQ/128))
     *                      = 10s * (SYSCLK_LSI_FREQ/128)
     */
    // IWDG_Reload = 10 * SYSCLK_LSI_FREQ / 128;
    // IWDG_Reload = 5 * SYSCLK_LSI_FREQ / 128; //!< [5s]
    IWDG_Reload = 1 * SYSCLK_LSI_FREQ / 128; //!< [1s]

    #elif (1) /* 6.4ms ~ 26214.4ms */
    /* IWDG counter clock: LSI/256 */
    IWDG_SetPrescalerDiv(IWDG_PRESCALER_DIV128);

    /** Set counter reload Value to obtain [25s] IWDG TimeOut.
     * Counter Reload Value = 25s / IWDG counter clock period
     *                      = 25s / (1/(SYSCLK_LSI_FREQ/256))
     *                      = 25s * (SYSCLK_LSI_FREQ/256)
     */
    IWDG_Reload = 25 * SYSCLK_LSI_FREQ / 256;
    #endif

    IWDG_CntReload(IWDG_Reload);

    /* Reload IWDG counter */
    IWDG_ReloadKey();

    /* Enable IWDG (the LSI oscillator will be enabled by hardware) */
    IWDG_Enable();
}

/**
  * @brief Reload IWDG counter
  * @param None
  * @retval None
  */
void IWDG_Feed(void)
{
    /* Reload IWDG counter */
    IWDG_ReloadKey();
}

#endif

/*******************************************************************************
 *                             SysClock Configuration
********************************************************************************/

#if (SYSCLK_SRC_HSI_ENABLE)

/**
 * @brief  Selects HSI as System clock source and configure
 *         HCLK, PCLK2 and PCLK1 prescalers.
 *
 * @retval 0      - success to start-up HSI/HSE
 *         others - fails to start-up HSI/HSE
 */
int SetSysClockToHSI(void)
{
    RCC_DeInit();

    RCC_EnableHsi(ENABLE);

    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufSet(FLASH_PrefetchBuf_EN);

    /* Flash 0 wait state */
    FLASH_SetLatency(FLASH_LATENCY_0);

    /* HCLK = SYSCLK */
    RCC_ConfigHclk(RCC_SYSCLK_DIV1);

    /* PCLK2 = HCLK */
    RCC_ConfigPclk2(RCC_HCLK_DIV1);

    /* PCLK1 = HCLK */
    RCC_ConfigPclk1(RCC_HCLK_DIV1);

    /* Select HSE as system clock source */
    RCC_ConfigSysclk(RCC_SYSCLK_SRC_HSI);

    /* Wait till PLL is used as system clock source */
    while (RCC_GetSysclkSrc() != 0x00) {
        ;
    }

    return 0;
}

#endif


#if (SYSCLK_SRC_HSE_ENABLE)
/**
 * @brief  Selects HSE as System clock source and configure
 *         HCLK, PCLK2 and PCLK1 prescalers.
 *
 * @retval 0      - success to start-up HSI/HSE
 *         others - fails to start-up HSI/HSE
 */
int SetSysClockToHSE(void)
{
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration
     * -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();

    /* Enable HSE */
    RCC_ConfigHse(RCC_HSE_ENABLE);

    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitHseStable();

    if (HSEStartUpStatus == SUCCESS) {
        /* Enable Prefetch Buffer */
        FLASH_PrefetchBufSet(FLASH_PrefetchBuf_EN);

        #if (HSE_Value <= 32000000)
        /* Flash 0 wait state */
        FLASH_SetLatency(FLASH_LATENCY_0);
        #else
        /* Flash 1 wait state */
        FLASH_SetLatency(FLASH_LATENCY_1);
        #endif

        /* HCLK = SYSCLK */
        RCC_ConfigHclk(RCC_SYSCLK_DIV1);

        /* PCLK2 = HCLK */
        RCC_ConfigPclk2(RCC_HCLK_DIV1);

        /* PCLK1 = HCLK */
        RCC_ConfigPclk1(RCC_HCLK_DIV1);

        /* Select HSE as system clock source */
        RCC_ConfigSysclk(RCC_SYSCLK_SRC_HSE);

        /* Wait till HSE is used as system clock source */
        while (RCC_GetSysclkSrc() != 0x04) {
            ;
        }
    } else {
        /* If HSE fails to start-up, the application will have wrong clock
           configuration. User can add here some code to deal with this error */
        // Error_Handler();
        return 1;
    }

    return 0;
}
#endif

#if (SYSCLK_SRC_PLL_ENABLE)
/**
 * @brief Set the SysClock To PLL object
 *
 * @param freq
 * @param src
 * @retval 0      - success to start-up HSI/HSE
 *         others - fails to start-up HSI/HSE
 */
int SetSysClockToPLL(uint32_t freq, uint8_t src)
{
    uint32_t pllsrc = (src == SYSCLK_PLLSRC_HSI ? RCC_PLL_SRC_HSI_DIV2 : RCC_PLL_SRC_HSE_DIV2);
    uint32_t pllmul;
    uint32_t latency;
    uint32_t pclk1div, pclk2div;

    /* HSE_VALUE == 8000000 is needed in this project! */
    ISS_ASSERT(HSE_VALUE == 8000000);

    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration
     * -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();

    if (src == SYSCLK_PLLSRC_HSI) {
        /* Enable HSI */
        RCC_EnableHsi(ENABLE);
    } else if (src == SYSCLK_PLLSRC_HSE) {
        /* Enable HSE */
        RCC_ConfigHse(RCC_HSE_ENABLE);

        /* Wait till HSE is ready */
        HSEStartUpStatus = RCC_WaitHseStable();

        if (HSEStartUpStatus != SUCCESS) {
            /* If HSE fails to start-up, the application will have wrong clock
               configuration. User can add here some code to deal with this
               error */
            // Error_Handler();
            return 1;
        }
    }

    switch (freq) {
        case 24000000:
            latency  = FLASH_LATENCY_0;
            pllmul   = RCC_PLL_MUL_6;
            pclk1div = RCC_HCLK_DIV1;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 36000000:
            latency  = FLASH_LATENCY_1;
            pllmul   = RCC_PLL_MUL_9;
            pclk1div = RCC_HCLK_DIV1;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 48000000:
            latency  = FLASH_LATENCY_1;
            pllmul   = RCC_PLL_MUL_12;
            pclk1div = RCC_HCLK_DIV2;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 56000000:
            latency  = FLASH_LATENCY_1;
            pllmul   = RCC_PLL_MUL_14;
            pclk1div = RCC_HCLK_DIV2;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 72000000:
            latency  = FLASH_LATENCY_2;
            pllmul   = RCC_PLL_MUL_18;
            pclk1div = RCC_HCLK_DIV2;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 96000000:
            latency  = FLASH_LATENCY_2;
            pllmul   = RCC_PLL_MUL_24;
            pclk1div = RCC_HCLK_DIV4;
            pclk2div = RCC_HCLK_DIV2;
            break;

        case 128000000:
            latency  = FLASH_LATENCY_3;
            pllmul   = RCC_PLL_MUL_32;
            pclk1div = RCC_HCLK_DIV4;
            pclk2div = RCC_HCLK_DIV2;
            break;

        case 144000000:
            /* must use HSE as PLL source */
            pllsrc   = RCC_PLL_SRC_HSE_DIV1;

            latency  = FLASH_LATENCY_4;
            pllmul   = RCC_PLL_MUL_18;
            pclk1div = RCC_HCLK_DIV4;
            pclk2div = RCC_HCLK_DIV2;
            break;

        default:
            Error_Handler();
    }

    FLASH_SetLatency(latency);

    /* HCLK = SYSCLK */
    RCC_ConfigHclk(RCC_SYSCLK_DIV1);

    /* PCLK2 = HCLK */
    RCC_ConfigPclk2(pclk2div);

    /* PCLK1 = HCLK */
    RCC_ConfigPclk1(pclk1div);

    RCC_ConfigPll(pllsrc, pllmul);

    /* Enable PLL */
    RCC_EnablePll(ENABLE);

    /* Wait till PLL is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRD) == RESET);

    /* Select PLL as system clock source */
    RCC_ConfigSysclk(RCC_SYSCLK_SRC_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while (RCC_GetSysclkSrc() != 0x08);



    #if (0U) /*!< ↓↓↓ 直接配置寄存器，目前尚未测试通过 */

    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
    uint32_t Flash_Latency_Temp = 0;
    uint32_t PLL_Temp = 0;
    uint32_t System_Temp = 0;

    if (src == SYSCLK_PLLSRC_HSI) {
        /* Enable HSI */
        RCC->CTRL |= ((uint32_t)RCC_CTRL_HSIEN);
    } else if (src == SYSCLK_PLLSRC_HSE) {
        /* Enable HSE */
        RCC->CTRL |= ((uint32_t)RCC_CTRL_HSEEN);

        /* Wait till HSE is ready and if Time out is reached exit */
        do {
            HSEStatus = RCC->CTRL & RCC_CTRL_HSERDF;
            StartUpCounter++;
        } while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

        if ((RCC->CTRL & RCC_CTRL_HSERDF) != RESET) {
            HSEStatus = (uint32_t)0x01;
        } else {
            HSEStatus = (uint32_t)0x00;
        }

        if (HSEStatus != (uint32_t)0x01) {
            /* If HSE fails to start-up, the application will have wrong clock
               configuration. User can add here some code to deal with this error */
            Error_Handler();
        }
    }

    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
    switch (freq) {
        case 24000000:
            latency  = FLASH_LATENCY_0;
            pllmul   = RCC_PLL_MUL_6;
            pclk1div = RCC_HCLK_DIV1;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 36000000:
            latency  = FLASH_LATENCY_1;
            pllmul   = RCC_PLL_MUL_9;
            pclk1div = RCC_HCLK_DIV1;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 48000000:
            latency  = FLASH_LATENCY_1;
            pllmul   = RCC_PLL_MUL_12;
            pclk1div = RCC_HCLK_DIV2;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 56000000:
            latency  = FLASH_LATENCY_1;
            pllmul   = RCC_PLL_MUL_14;
            pclk1div = RCC_HCLK_DIV2;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 72000000:
            latency  = FLASH_LATENCY_2;
            pllmul   = RCC_PLL_MUL_18;
            pclk1div = RCC_HCLK_DIV2;
            pclk2div = RCC_HCLK_DIV1;
            break;

        case 96000000:
            latency  = FLASH_LATENCY_2;
            pllmul   = RCC_PLL_MUL_24;
            pclk1div = RCC_HCLK_DIV4;
            pclk2div = RCC_HCLK_DIV2;
            break;

        case 128000000:
            latency  = FLASH_LATENCY_3;
            pllmul   = RCC_PLL_MUL_32;
            pclk1div = RCC_HCLK_DIV4;
            pclk2div = RCC_HCLK_DIV2;
            break;

        case 144000000:
            /* Must use HSE as PLL source */
            pllsrc   = RCC_PLL_SRC_HSE_DIV1;

            latency  = FLASH_LATENCY_4;
            pllmul   = RCC_PLL_MUL_18;
            pclk1div = RCC_HCLK_DIV4;
            pclk2div = RCC_HCLK_DIV2;
            break;

        default:
            Error_Handler();
    }

    /* Enable Prefetch Buffer */
    FLASH->AC |= FLASH_AC_PRFTBFEN;

    /* Flash 2 wait state */
    Flash_Latency_Temp = FLASH->AC;
    Flash_Latency_Temp &= (uint32_t)((uint32_t)~FLASH_AC_LATENCY);
    Flash_Latency_Temp |= (uint32_t)latency;
    FLASH->AC = Flash_Latency_Temp;

    /* HCLK = SYSCLK */
    RCC->CFG |= (uint32_t)RCC_CFG_AHBPRES_DIV1;

    /* PCLK2 = HCLK */
    RCC->CFG |= (uint32_t)pclk2div;

    /* PCLK1 = HCLK */
    RCC->CFG |= (uint32_t)pclk1div;

    /*  PLL configuration: PLLCLK = pllsrc * pllmul */
    PLL_Temp = RCC->CFG;
    PLL_Temp &= (uint32_t)((uint32_t) ~(RCC_CFG_PLLSRC | RCC_CFG_PLLHSEPRES | RCC_CFG_PLLMULFCT));
    PLL_Temp |= (uint32_t)(pllsrc | pllmul);
    RCC->CFG = PLL_Temp;

    /* Enable PLL */
    RCC->CTRL |= RCC_CTRL_PLLEN;

    /* Wait till PLL is ready */
    while ((RCC->CTRL & RCC_CTRL_PLLRDF) == 0) {
    }

    /* Select PLL as system clock source */
    System_Temp = RCC->CFG;
    System_Temp &= (uint32_t)((uint32_t) ~(RCC_CFG_SCLKSW));
    System_Temp |= (uint32_t)RCC_CFG_SCLKSW_PLL;
    RCC->CFG = System_Temp;

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFG & (uint32_t)RCC_CFG_SCLKSTS) != (uint32_t)0x08) {
    }

    #endif

    return 0;
}
#endif

/**
 * @brief System Clock Configuration
 * @param None
 * @retval None
 */
void SysClock_Configuration(void)
{
    /* Enable HSE-PLL */
    if (SetSysClockToPLL(144000000, SYSCLK_PLLSRC_HSE) != 0) {
        /* Enable HSI-PLL */
        (void)SetSysClockToPLL(72000000, SYSCLK_PLLSRC_HSI);
    }

    /* Enable Clock Security System(CSS): this will generate an NMI exception when HSE clock fails */
    RCC_EnableClockSecuritySystem(ENABLE);

    SystemCoreClockUpdate();
}

/*******************************************************************************
 *                             SysTick Configuration
********************************************************************************/

/**
 * @brief SysTick Configuration
 * @param None
 * @retval None
 */
void SysTick_Configuration(void)
{
    /* Configure the SysTick to have interrupt in 1ms time basis*/
    RCC_GetClocksFreqValue(&sRCC_ClockFreq);

    if (SysTick_Config(sRCC_ClockFreq.HclkFreq / 1000)) {
        Error_Handler();
    }

    STIMER_RESET();
}

/*******************************************************************************
 *                             GPIO Configuration
********************************************************************************/

#if (LPM_ENABLE)
/**
 * @brief PA0 WakeUp interrupt configuration
 * @param None
 * @retval None
 */
void WakeUpPin_Configuration(void)
{
    GPIO_InitType GPIO_InitStructure;
    EXTI_InitType EXTI_InitStructure;
    NVIC_InitType NVIC_InitStructure;

    #if defined(AK601)

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);

    /* Configure GPIO pin as input pull-up : PA9 */
    GPIO_InitStructure.Pin = GPIO_PIN_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_INPUT;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure INT1 EXTI Line to input Pin: PA0 */
    GPIO_ConfigEXTILine(GPIOA_PORT_SOURCE, GPIO_PIN_SOURCE0);

    /* Configure INT1 EXTI line */
    EXTI_InitStructure.EXTI_Line    = EXTI_LINE0;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitPeripheral(&EXTI_InitStructure);

    /* Configure one bit for preemption priority */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    /* Enable the EXIT Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel                   = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    #endif

}

/**
 * @brief Low Power Configuration of GPIO Driver.
 *
 * @param[in] void
 * @return void
 */
void GPIO_LP_Configuration(void)
{
    // TBD...
    // GPIO_InitType GPIO_InitStructure;

    // /* Configure GPIO pin Default AIN Level */
    // GPIO_InitStructure.Pin = GPIO_PIN_X;
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    // GPIO_InitStructure.GPIO_Speed = GPIO_INPUT;
    // GPIO_InitPeripheral(GPIOX, &GPIO_InitStructure);

    #if (LOW_POWER_MODE != STANDBY_MODE)
    /* Configure GPIO pin Default AIN Level */
    GPIO_InitType GPIO_InitStructure;

    #if defined(AK601)
    /* Configure GPIO pin Default AIN Level : PA9 | PA10 | PA11 */
    GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_INPUT;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure GPIO pin Default AIN Level : PB13 */
    GPIO_InitStructure.Pin = GPIO_PIN_13;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);


    /* Configure GPIO pin Default Output Level */
    GPIO_WriteBit(GPIOA, GPIO_PIN_4, Bit_RESET); //!<
    GPIO_WriteBit(GPIOA, GPIO_PIN_5, Bit_RESET); //!<
    GPIO_WriteBit(GPIOA, GPIO_PIN_6, Bit_RESET); //!<
    GPIO_WriteBit(GPIOA, GPIO_PIN_7, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_2, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_10, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_12, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_3, Bit_SET); //!< CAN1_STB = HIGH

    /* Configure GPIO pin : PA4 | PA5 | PA6 | PA7 */
    GPIO_InitStructure.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure GPIO pin : PB2 | PB10 | PB12 | PB3 */
    GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_10 | GPIO_PIN_12 | GPIO_PIN_3;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    #endif

    #endif
}
#endif

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
void GPIO_Configuration(void)
{
    GPIO_InitType GPIO_InitStructure;

    /* GPIO Ports Clock Enable */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOD,
                            ENABLE);
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Disable the JTAG interface and enable the SWJ interface
        This operation is not necessary for Connectivity Line devices since
        SPI3 I/Os can be remapped on other GPIO pins */
    GPIO_ConfigPinRemap(GPIO_RMP_SW_JTAG_SW_ENABLE, ENABLE);  //!< Release PA15, PB3, PB4
    // GPIO_ConfigPinRemap(GPIO_RMP_PD01, ENABLE);  //!< Release PD0/PD1

    #if defined(AK601)

    /** Configure Input GPIO pins:
     * PB13	DI	K3	座椅传感器
     * PA9	DI	K4  急停按键 (低功耗唤醒)
     * PA10	DI	K2  机械钥匙
     * PA11	DI	K1  按键启动
     */
    NVIC_DisableIRQ(EXTI9_5_IRQn);

    /* Configure GPIO pin as input pull-up : PA9 | PA10 | PA11 */
    GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_INPUT;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure GPIO pin as input pull-up : PB13 */
    GPIO_InitStructure.Pin = GPIO_PIN_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /** Configure Output GPIO pins:
     * PA4	DO	A2  前大灯远
     * PA5	DO	A4  右转向灯
     * PA6	DO	A6  尾灯
     * PA7	DO	A5  倒车喇叭
     * PB2	DO	A3  左转向灯
     * PB10	DO	A1  前大灯近
     * PB12	DO	H1	喇叭
     *
     * PB3	DO	CAN1_STB
     */

    /* Configure GPIO pin Default Output Level */
    GPIO_WriteBit(GPIOA, GPIO_PIN_4, Bit_RESET); //!<
    GPIO_WriteBit(GPIOA, GPIO_PIN_5, Bit_RESET); //!<
    GPIO_WriteBit(GPIOA, GPIO_PIN_6, Bit_RESET); //!<
    GPIO_WriteBit(GPIOA, GPIO_PIN_7, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_2, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_10, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_12, Bit_RESET); //!<
    GPIO_WriteBit(GPIOB, GPIO_PIN_3, Bit_SET); //!< CAN1_STB = HIGH

    /* Configure GPIO pin : PA4 | PA5 | PA6 | PA7 */
    GPIO_InitStructure.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    /* Configure GPIO pin : PB2 | PB10 | PB12 | PB3 */
    GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_10 | GPIO_PIN_12 | GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    #endif

}

/*******************************************************************************
 *                           LOG Initialization
********************************************************************************/

#if (LPM_ENABLE)
/**
 * @brief DeInitialization
 * @param None
 * @retval None
 */
void LOG_LP_Configuration(void)
{
    /*!< Hardware deinitialization -------------------------------------*/
    #if (1)
    /** UART6 GPIO Configures
    PB0 ------> UART6_TX
    PB1 ------> UART6_RX
    */
    GPIO_InitType GPIO_InitStructure;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);

    /* Configure UART6 Tx as input ain */
    GPIO_InitStructure.Pin = GPIO_PIN_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_INPUT;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Configure UART6 Rx as input ain */
    GPIO_InitStructure.Pin = GPIO_PIN_1;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    /* Not Remap UART6 GPIOs */
    GPIO_ConfigPinRemap(GPIO_RMP3_UART6, DISABLE);

    #endif

    NVIC_DisableIRQ(UART_IRQn_PRINTF); //!< Printf interrupt disabled

}
#endif

/**
 * @brief Initialization
 * @param None
 * @retval None
 */
void LOG_Configuration(void)
{
    UartX_Configuration(UART_CHANNEL_PRINTF, UART_BAUDRATE_PRINTF);
}

/*******************************************************************************
 *                               Hardware System
********************************************************************************/

/**
 * @brief Board Initialization
 * @param None
 * @retval None
 */
void SYS_BOARD_INIT(void)
{
    /* SystemClock configuration */
    SysClock_Configuration();

    /* Backup configuration */
    Backup_Configuration();

}

/*******************************************************************************
 *                              System sTask
********************************************************************************/

#if (LPM_ENABLE)

/**
 * @brief Software/Hardware deinitialization.
 *
 * @param[in] void
 * @return void
 */
void SYS_DeInit(void)
{
    /* I/O low power configuration */
    GPIO_LP_Configuration();
}

#endif

/**
 * @brief Software/Hardware Initialization.
 *
 * @param[in] void
 * @return void
 */
void SYS_Init(void)
{
    /*!< Hardware initialization ---------------------------------------*/

    /* SysTick configuration */
    SysTick_Configuration();

    /* I/O configuration */
    GPIO_Configuration();

    /* Printf Configuration */
    LOG_Configuration();

    /* IWDG configuration */
    #if (IWDG_ENABLE)
    IWDG_Configuration();
    #endif


    /* SSM Configuration */
    SSM_Init();

    /*!< Software initialization ---------------------------------------*/
    memset((void *)&Project_Global, 0, sizeof(Project_Global));

    #if (!LPM_ENABLE)
    USER_PRINTF("\r\n");
    USER_PRINTF("/*\r\n ***********************************\r\n");
    USER_PRINTF(" * @brief: Application.h \r\n");
    USER_PRINTF(" * @reversion: %d.%d.%d.%d\r\n",
                FIRMWARE_REVISION_MAJOR,
                FIRMWARE_REVISION_MINOR,
                FIRMWARE_REVISION_REVISION,
                FIRMWARE_REVISION_BUILD);
    USER_PRINTF(" * @date: %s\r\n", __DATE__);
    USER_PRINTF(" * @hardware: %s\r\n", HARDWARE_VERSION);
    USER_PRINTF(" * @O.o [%dHz]\r\n", sRCC_ClockFreq.SysclkFreq);
    #if (IWDG_ENABLE)
    USER_PRINTF(" * @LSI: [%dHz]\r\n", SYSCLK_LSI_FREQ);
    #endif
    USER_PRINTF(" * @copyright: %s\r\n", MANUFACTURER);
    USER_PRINTF(" ***********************************\r\n */\r\n");

    Project_Global.Wake_Flag = BKP_DATA_WKUP_FLAG_DEFAULT;

    #else /*!< 正常模式初始化 END */

    #if (LOW_POWER_MODE == STANDBY_MODE)

    /* Check if the Wake-Up flag is Set */
    if (PWR_GetFlagStatus(PWR_WU_FLAG) != RESET) {
        /* Clear Wake Up flag */
        PWR_ClearFlag(PWR_WU_FLAG);

        LPM_IntCallback(BKP_DATA_WKUP_FLAG_EXIT_PA0);
    }

    #endif

    uint16_t Sys_Wake_Flag;

    Sys_Wake_Flag = LPM_WkupFlagRead();

    if (Sys_Wake_Flag == BKP_DATA_WKUP_FLAG_DEFAULT) {
        USER_PRINTF("\r\n");
        USER_PRINTF("/*\r\n ***********************************\r\n");
        USER_PRINTF(" * @brief: Application.h \r\n");
        USER_PRINTF(" * @reversion: %d.%d.%d.%d\r\n",
                    FIRMWARE_REVISION_MAJOR,
                    FIRMWARE_REVISION_MINOR,
                    FIRMWARE_REVISION_REVISION,
                    FIRMWARE_REVISION_BUILD);
        USER_PRINTF(" * @date: %s\r\n", __DATE__);
        USER_PRINTF(" * @hardware: %s\r\n", HARDWARE_VERSION);
        USER_PRINTF(" * @O.o [%dHz]\r\n", sRCC_ClockFreq.SysclkFreq);
        #if (IWDG_ENABLE)
        USER_PRINTF(" * @LSI: [%dHz]\r\n", SYSCLK_LSI_FREQ);
        #endif
        USER_PRINTF(" * @copyright: %s\r\n", MANUFACTURER);
        USER_PRINTF(" ***********************************\r\n */\r\n");

        /* Clear BKP */
        BKP_DeInit();
    }

    Project_Global.Wake_Flag = Sys_Wake_Flag;
    #endif /*!< 低功耗模式初始化 END */

    Project_Global.PRINTF_EN = BKP_ReadBkpData(BKP_REG_ID_PRINTF_EN);

}

/**
 * @brief Handle function for Application Driver layer
 *
 * @param[in] void
 * @return DRV Status
 */
void SYS_Handle(void *arg)
{
    (void)arg;

    #if (IWDG_ENABLE)
    IWDG_Feed();
    #endif

    for (int taskId = 0; taskId < TASK_ID_MAX_NUM; taskId++) {
        sTask_Active(taskId);
    }


    #if (LPM_ENABLE)

    /** @example 当满足特定条件后，对唤醒标志进行清零即可立即进入休眠模式。
     */
    if (Project_Global.Wake_Flag == 0) {
        Project_Global.Wake_Flag = LPM_WkupFlagRead();

        if (Project_Global.Wake_Flag == BKP_DATA_WKUP_FLAG_DEFAULT) {
            USER_PRINTF("# Go Sleep #\r\n");

            for (int taskId = 0; taskId < TASK_ID_MAX_NUM; taskId++) {
                sTask_Inactive(taskId);
            }
        }
    }

    #endif

}

/* PRQA S 2740, 2870, 2885, 3383 -- */

/* END OF FILE ---------------------------------------------------------------*/
